package cz.muni.fi.pa165.soccergamesarchive.persistencelayer.daos;

import cz.muni.fi.pa165.soccergamesarchive.businesslayer.dtos.TeamStatisticsDTO;
import cz.muni.fi.pa165.soccergamesarchive.errorhandling.ErrorHandler;
import cz.muni.fi.pa165.soccergamesarchive.persistencelayer.entities.Team;
import cz.muni.fi.pa165.soccergamesarchive.persistencelayer.querybuilders.entity.TeamCriteriaQueryBuilder;
import cz.muni.fi.pa165.soccergamesarchive.validators.ValidatorTest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import static cz.muni.fi.pa165.soccergamesarchive.errorhandling.ErrorHandler.createErrorMessage;
import static cz.muni.fi.pa165.soccergamesarchive.validators.TeamValidator.validateTeam;

/**
 *
 * @author Martin
 *
 * <strong>Remake by Michal Ďuriš</strong>
 */
@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless
public class TeamDAOImpl implements TeamDAO {

    @PersistenceContext(unitName = "SoccerGamesArchivePersistenceUnit")
    private EntityManager entityManager;

    private final TeamCriteriaQueryBuilder queryBuilder = new TeamCriteriaQueryBuilder(
            Team.class);

    public TeamDAOImpl() {
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public Team addTeam(Team team) throws RuntimeException {

        int teamStatus = validateTeam(team,
                ValidatorTest.NULL_TEST,
                ValidatorTest.CORRECT_RECORD_TEST);

        if (teamStatus != ErrorHandler.NO_ERROR) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), teamStatus));
        }

        entityManager.persist(team);
        return team;
    }

    @Override
    public Team changeTeam(Team team) throws RuntimeException {

        int teamStatus = validateTeam(team,
                ValidatorTest.FULL_TEST);

        if (teamStatus != ErrorHandler.NO_ERROR) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), teamStatus));
        }
        return entityManager.merge(team);
    }

    @Override
    public void removeTeam(Team team) throws RuntimeException {

        int teamStatus = validateTeam(team,
                ValidatorTest.NULL_TEST,
                ValidatorTest.ID_TEST);

        if (teamStatus != ErrorHandler.NO_ERROR) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), teamStatus));
        }

        entityManager.remove(entityManager.merge(team));
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public List<Team> getAllTeams() {

        return entityManager.createNamedQuery("Team.getAllTeams", Team.class)
                .getResultList();
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public List<Team> getLimitedNumberOfTeams(int offset, int limit,
                                              Map<String, Object> filterMap,
                                              Map<String, Boolean> orderMap)
            throws RuntimeException {

        if (offset < 0 || limit <= 0) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), ErrorHandler.ILLEGAL_VALUE_ERROR));
        }

        return queryBuilder.createEntitySelectCriteriaQuery(entityManager,
                filterMap, orderMap).setFirstResult(offset).setMaxResults(limit)
                .getResultList();
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public int getFilteredTeamsCount(Map<String, Object> filterMap) throws
            RuntimeException {

        return queryBuilder.createEntityCountCriteriaQuery(entityManager,
                filterMap).getSingleResult().intValue();
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public Team getTeamById(Long teamId) throws RuntimeException {
        if (teamId == null || teamId < 0) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), ErrorHandler.ILLEGAL_VALUE_ERROR));
        }

        return entityManager.find(Team.class, teamId);
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public List<Integer> getAvaliableShirtNumbers(Team team) throws
            RuntimeException {

        int teamStatus = validateTeam(team,
                ValidatorTest.NULL_TEST, ValidatorTest.ID_TEST);

        if (teamStatus != ErrorHandler.NO_ERROR) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), teamStatus));
        }

        List<Integer> availableShirtNumbers = new ArrayList<>();

        for (int i = 1; i < 100; i++) {
            availableShirtNumbers.add(i);
        }

        List<Integer> occupiedShirtNumbers = entityManager.createNamedQuery(
                "Team.getOccupiedShirtNumbers", Integer.class).setParameter(
                        "teamID", team.getId()).getResultList();

        if (occupiedShirtNumbers != null && !occupiedShirtNumbers.isEmpty()) {

            for (Integer occupiedShirtNumber : occupiedShirtNumbers) {
                availableShirtNumbers.remove(occupiedShirtNumber - 1);
            }
        }

        return availableShirtNumbers;
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public List<Team> findTeam(String searchString) throws RuntimeException {

        if (searchString == null || searchString.isEmpty()) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), ErrorHandler.ILLEGAL_VALUE_ERROR));
        }

        TypedQuery<Team> teamTypedQuery = entityManager.createNamedQuery(
                "Team.findTeam", Team.class);

        teamTypedQuery.setParameter("searchString", "%" + searchString.trim()
                .toLowerCase() + "%");

        return teamTypedQuery.getResultList();
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @Override
    public List<TeamStatisticsDTO> getTeamStatistics(Team team) throws
            RuntimeException {

        int teamStatus = validateTeam(team,
                ValidatorTest.NULL_TEST, ValidatorTest.ID_TEST);

        if (teamStatus != ErrorHandler.NO_ERROR) {
            throw new IllegalArgumentException(createErrorMessage(
                    this.getClass().getName(), teamStatus));
        }

        TeamStatisticsDTO statistics = new TeamStatisticsDTO();
        List<TeamStatisticsDTO> statisticsList = new ArrayList<>();

        TypedQuery<Object[]> query = entityManager.createNamedQuery(
                "Team.getStatistics", Object[].class);

        query.setParameter("teamID", team.getId());

        Object[] queryResult = query.getSingleResult();

        statistics.setWins(((Long) queryResult[0]).intValue());
        statistics.setDrawns(((Long) queryResult[1]).intValue());
        statistics.setLosts(((Long) queryResult[2]).intValue());
        statistics.setGoalsScored(((Long) queryResult[3]).intValue());
        statistics.setGoalsConceded(((Long) queryResult[4]).intValue());

        statisticsList.add(statistics);

        return statisticsList;
    }

}
